package model;

import java.util.Random;

/**
 * The game board stores all the fields and all the cards. It offers a lot of methods 
 * for finding an exact card/field or a specific data within.
 * @author Group 20.
 */
public class GameBoard 
{
	public static Field fields[] = new Field[40];
	public static Card[] cards = new Card[33];
	public static String currentCardDescription = "";

	/**
	 * All the fields/cards are created.
	 * @param players
	 */
	static public void Initialize(Player[] players)
	{
		fields[0] = (new Misc(1, "Start", "Receive 4000"));
		fields[1] = (new Street(2, "Roedovrevej","Price: 1200.-", 1200, 50, 250, 750, 2250, 4000, 6000, 1000,"Blue"));
		fields[2] = (new Chance(3, "Chance","Chance"));
		fields[3] = (new Street(4, "Hvidovrevej","Price: 1200.-", 1200, 50, 250, 750, 2250, 4000, 6000, 1000, "Blue"));
		fields[4] = (new TaxSelect(5, "Income tax","10% or 4000", 4000));
		fields[5] = (new Shipping(6,"LB ferries","Price: 4000.-", 4000));//
		fields[6] = (new Street(7, "Roskildevej", "Price: 2000.-", 2000, 100, 600, 1800, 5400, 8000, 11000, 1000,"Pink"));
		fields[7] = (new Chance(8, "Chance","Chance"));
		fields[8] = (new Street (9, "Valby Langgade", "Price: 2000.-", 2000, 100, 600, 1800, 5400, 8000, 11000, 1000, "Pink"));
		fields[9] = (new Street (10, "Allegade", "Price: 2400.-", 2400, 150, 800, 2000, 6000, 9000, 12000, 1000, "Pink"));
		fields[10] = (new Misc(11, "Jail", "Jail"));
		fields[11] = (new Street (12, "Frederiksberg Alle", "Price: 2800.-", 2800, 200, 1000, 3000, 9000, 12500, 15000, 2000, "Green"));
		fields[12] = (new Brewery(13,"Tuborg","Price: 4000.-", 4000)); // ---------------------
		fields[13] = (new Street (14, "Bulowsvej", "Price: 2800.-", 2800, 200, 1000, 3000, 9000, 12500, 15000, 2000, "Green"));
		fields[14] = (new Street (15, "Gl. Kongevej", "Price: 3200.-", 3200, 250, 1250, 3750, 10000, 14000, 18000, 2000, "Green"));
		fields[15] = (new Shipping(16,"D.F.D.S","Price: 4000.-", 4000)); // --------------------------------------
		fields[16] = (new Street (17, "Bernstorfsvej", "Price: 3600.-", 3600, 300, 1400, 4000, 11000, 15000, 19000, 2000, "Grey"));
		fields[17] = (new Chance(18, "Chance","Chance"));
		fields[18] = (new Street (19, "Hellerupvej", "Price: 3600.-",3600, 300, 1400, 4000, 11000, 15000, 19000, 2000, "Grey"));
		fields[19] = (new Street (20, "Strandvej", "Price: 4000.-", 4000, 350, 1600, 4400, 12000, 16000, 20000, 2000, "Grey"));
		fields[20] = (new Refuge(21, "Refuge", "Refuge", 0));
		fields[21] = (new Street (22, "Trianglen", "Price: 4400.-", 4400, 350, 1800, 5000, 14000, 17500, 21000, 3000,"Red"));
		fields[22] = (new Chance(23, "Chance","Chance"));
		fields[23] = (new Street (24, "Oesterbrogade", "Price: 4400.-", 4400, 350, 1800, 5000, 14000, 17500, 21000, 3000, "Red"));
		fields[24] = (new Street (25, "Groenningen", "Price: 4800.-", 4800, 400, 2000, 6000, 15000, 18500, 22000, 3000, "Red"));
		fields[25] = (new Shipping(26,"Molslinien","Price: 4000.-", 4000)); // ----------------------
		fields[26] = (new Street (27, "Bredgade", "Price: 5200.-", 5200, 450, 2200, 6600, 16000, 19500, 23000, 3000, "White"));
		fields[27] = (new Street (28, "Kgs. Nytorv", "Price: 5200.-", 5200, 450, 2200, 6600, 16000, 19500, 23000, 3000, "White"));
		fields[28] = (new Brewery(29,"Carlsberg","Price: 4000.-", 4000)); // -------------------
		fields[29] = (new Street (30, "Oestergade", "Price: 5600.-", 5600, 500, 2400, 7200, 17000, 20500, 24000, 3000, "White"));
		fields[30] = (new GoToJail(31, "Go to Jail", "Go to Jail"));
		fields[31] = (new Street (32, "Amagertorv", "Price: 6000.-", 6000, 550, 2600, 7800, 18000, 22000, 25000, 4000, "Yellow"));
		fields[32] = (new Street (33, "Vimmelskaftet", "Price: 6000.-", 6000, 550, 2600, 7800, 18000, 22000, 25000, 4000,"Yellow"));
		fields[33] = (new Chance(34, "Chance","Chance"));
		fields[34] = (new Street (35, "Nygade", "Price: 6400.-", 6400, 600, 3000, 9000, 20000, 24000, 28000, 4000, "Yellow"));
		fields[35] = (new Shipping(36,"Scandlines","Price: 4000.-", 4000)); // ---------------------
		fields[36] = (new Chance(37, "Chance","Chance"));
		fields[37] = (new Street (38, "Frederiksberggade", "Price: 7000.-", 7000, 700, 3500, 10000, 22000, 26000, 30000, 4000,"Purple"));
		fields[38] = (new Taxes(39, "Federal Tax", "Pay 2000.-",2000));
		fields[39] = (new Street (40, "Raadhuspladsen", "Price: 8000.-", 8000, 1000, 4000, 12000, 28000, 34000, 40000, 4000, "Purple"));
		
		
		cards[0] = new FromBank("Bank pays you dividend of 1000.-", 1000);
		cards[1] = new FromBank("You have won a competition in the local newspaper. Receive 1000.-", 1000);
		cards[2] = new FromBank("Your premium bond has given a good return, collect 1000.- from the bank", 1000);
		cards[3] = new FromBank("Your premium bond has given a good return, collect 1000.- from the bank", 1000);
		cards[4] = new FromBank("You have won on a scrathcard, receive 200.-", 200);
		cards[5] = new FromBank("Due to a time of high prices, you have received a pay raise. Receive 1000.-", 1000);
		cards[6] = new FromBank("You have won a small price in the lottery, receive 500.-", 500);
		cards[7] = new FromBank("You have paid to much in taxes, you get money back. Receive 3000.-", 3000);
		cards[8] = new FromBank("Collect dividend of 1000.- from the bank.", 1000);
		cards[9] = new FromBank("Collect dividend of 1000.- from the bank.", 1000);
		cards[10] = new ValueDependency("You have received a grant for the needy. Receive 40000.- if all of your combined values is worth less than 15000.-", 40000, 15000);
		cards[11] = new PlayerToPlayer("Today is your birthday! Collect 200.- from all the players", 200, players);
		cards[12] = new ToBank("On your trip abroad, you brought back to many cigarettes. Pay duty of 200.-", 200);
		cards[13] = new ToBank("You have received your dentist bill. Pay 2000.-", 2000);
		cards[14] = new ToBank("Pay your car insurance: 1000.-", 1000);
		cards[15] = new ToBank("Pay 3000.- for the reparation of your car.", 3000);
		cards[16] = new ToBank("Pay 3000.- for the reparation of your car..", 3000);
		cards[17] = new ToBank("You have received a ticket for speeding. Pay 1000.-", 1000);
		cards[18] = new ToBank("You have parked in a no parking zone, and received a parking ticket. Pay 200.-", 200);
		cards[19] = new HouseHotelDependency("The property tax has increased, the additional costs are: 800.- per hus,  2300.- per hotel.", 0, 800, 2300);
		cards[20] = new HouseHotelDependency("The oil prices has gone up, you have to pay:   500.- per hus,   2000.- per hotel.", 0, 500, 2000);
		cards[21] = new ToClosestShipping("Advance to the nearest Ferry and pay the owner twice the rental he is entitled to. If the Ferry is unowned you may buy it.", fields);
		cards[22] = new ToClosestShipping("Advance to the nearest Ferry and pay the owner twice the rental he is entitled to. If the Ferry is unowned you may buy it.", fields);
		cards[23] = new ToLocation("Advance to start", 1, 4000);
		cards[24] = new ToLocation("Advance to Groeninngen, if you pass start you will collect 4000.- ", 25, 4000);
		cards[25] = new ToLocation("Take the LB-ferries. If you pass start you will collect the 4000.-", 6, 4000);
		cards[26] = new ToLocation("Advance to Frederiksberg All�. If you pass start you will collect 4000.-", 12, 4000);
		cards[27] = new ToLocation("Advance to Raadhuspladsen", 40, 0);
		cards[28] = new MoveBack("Go back 3 spaces");
		cards[29] = new ToJailCard("You have been arrested and you are send to jail. You will not collect 4000.- if you pass start.", 11);
		cards[30] = new ToJailCard("You have been arrested and you are send to jail. You will not collect 4000.- if you pass start.", 11);
		cards[31] = new GetOutOfJail("In honor of the kings birthday, you are pardoned from jail. You can keep this card until you need it.");
		cards[32] = new GetOutOfJail("In honor of the kings birthday, you are pardoned from jail. You can keep this card until you need it.");
	}
	
	/**
	 * Get the current card's description.
	 * @return: Card description
	 */
	public static String getCurrentCardDescription()
	{
		return currentCardDescription;
	}
	
	/**
	 * Get all Fields.
	 * @return: Array of Field objects
	 */
	public static Field[] getAllFields() 
	{
		return fields;
	}
	
	/**
	 * Updates the card with player info before the card takes action.
	 * @param player: Current player object
	 */
	public static void updateDependencyCards(Player player)
	{
		((HouseHotelDependency) cards[19]).setNumOfHouses(getTotalNumOfHouses(player));
		((HouseHotelDependency) cards[20]).setNumOfHouses(getTotalNumOfHouses(player));
		((ValueDependency) cards[10]).setValueOfAssets(getValueAllAssets(player));
	}
	
	/**
	 * Calculates all the player assets
	 * @param player: Current player object
	 * @return: Total amount of assets
	 */
	public static int getValueAllAssets(Player player)
	{
		int totalValueAssests = 0;
		
		for(Field field: fields)
		{
			if (field instanceof Ownable)
			{
				Ownable ownableField = ((Ownable)field);
				
				if(ownableField.getOwner() == player)
				{
					totalValueAssests = totalValueAssests + ownableField.valueOfAssets();
				}
			}
		}
		return totalValueAssests;
	}
	
	/**
	 * Calculates the amount houses owned (by the player)
	 * @param player: Player object
	 * @return: Number of houses
	 */
	public static int getTotalNumOfHouses(Player player)
	{
		int totalNumOfhouses = 0;
		
		for(Field field: fields)
		{
			if (field instanceof Street)
			{
				Street street = ((Street)field);
				
				if(street.getOwner() == player)
				{
					totalNumOfhouses = totalNumOfhouses + street.getNumOfHouses();
				}
			}
		}
		return totalNumOfhouses;
	}
	
	/**
	 * Calculates the amount of hotels owned (by the eplayer)
	 * @param player: Player object
	 * @return: Number of hotels owned.
	 */
	public static int getTotalNumOfHotels(Player player)
	{
		int totalNumOfHotels = 0;
		
		for(Field field: fields)
		{
			if (field instanceof Street)
			{
				Street street = ((Street)field);
				
				if(street.getOwner() == player)
				{
					totalNumOfHotels = totalNumOfHotels + street.getNumOfHotels();
				}
			}
		}
		return totalNumOfHotels;
	}
	
	/**
	 * Get a specific field.
	 * @param fieldNumber: Field number
	 * @return: The requested Field object.
	 */
	public static Field getField(int fieldNumber)
	{	
		
		for (Field field : fields) 
		{
			
			if(field.getNumber() == fieldNumber)
			return field;
		}	
		return null;	
	}
	
	/**
	 * Clears owner and Pawn info on every field owned by a specific player
	 * @param player: Player object
	 */
	public static void clearOwnedFields(Player player)
	{
		for (Field field : fields) 
		{
			if (field instanceof Ownable)
			{
				Ownable ownableField = (Ownable)field;
				if (ownableField.getOwner() == player)
				{
					ownableField.clear();
				}
			}
		}	
	}
	
	/**
	 * Picks a card from the stack. The order of the cards is random. When a card is picked it is marked a "picked".
	 * All the card has to be picked before a specific card can be picked again.
	 * @param players: Player object
	 * @return: A random Card object.
	 */
	public static Card getCard(Player[] players)
	{
		Random randomCard = new Random();
		// 1. Get num of non-drawn cards
		int numOfAvailableCards = 0;
		for(Card card:cards)
		{
			if(!card.isDrawn())
				numOfAvailableCards++;
		}
		
		// 2. If all cards are drawn: make them drawnable again
		if(numOfAvailableCards == 0)
		{
			// 2.1 Find number of GetOutOfJail card in the players hands
			int jailCardsOut = 0;
			for(Player player: players)
			{
				if(player.hasOutOfJailCard())
				{
					jailCardsOut++;
				}
			}
			
			// 2.2 Make cards drawnable, except from GetOutOfJail cards, which have been drawn but not used
			for(Card card: cards)
			{
				if(!(card instanceof GetOutOfJail))
				{
					card.setDrawn(false);
					numOfAvailableCards++;
				}
				else if (jailCardsOut < 2)
				{
					card.setDrawn(false);
					jailCardsOut++;
					numOfAvailableCards++;
				}
			}
		}
		
		// 3. Add array with exactly that number of spaces
		Card availableCards[] = new Card[numOfAvailableCards];
		
		// 4. Fill up the new array with avaible card objects
		int availableCardIndex = 0;
		for(Card card:cards)
		{
			if(!card.isDrawn())
			{
				availableCards[availableCardIndex] = card;
				availableCardIndex++;
			}
		}
		
		// 5. Pick a random number from 0 - number of available cards
		int randomCardIndex = randomCard.nextInt(numOfAvailableCards);
		
		// 6. Draw a random available card
		Card drawnCard = availableCards[randomCardIndex];
		
		// 7. Remember that the card is drawn
		drawnCard.setDrawn(true);

		// 8. Save current card description
		currentCardDescription = drawnCard.getDescription();
		
		// 9. Return the drawn card
		return drawnCard; 
	}
	
	
	/**
	 * Calculates the amount of field owned with the same type as the field compared to.
	 * @param field: Field object that has to be compared to.
	 * @return: Number of fields owned of Field type.
	 */
	public static int numFieldType(Field field)
	{
		int num = 0;

		if (field instanceof Ownable)
		{
			int ownerNumber = ((Ownable)field).getOwnerNumber();
			
			if (ownerNumber !=-1)
			{
				// Shipping
				if (field instanceof Shipping)
				{
					for (Field field_temp : fields)
					{
						if (field_temp instanceof Shipping)
						{
							if (((Shipping) field_temp).getOwnerNumber() == ownerNumber)
							num++;
						}
					}
		
				}
				
				// Brewery
				if (field instanceof Brewery)
				{
					for (Field field_temp : fields)
					{
						if (field_temp instanceof Brewery)
						{
							if (((Brewery) field_temp).getOwnerNumber() == ownerNumber)
							num++;
						}
					}
			
				}
				
				// Street
				if (field instanceof Street)
				{
					String groupColor = ((Street) field).getGroupColor();
					
					for (Field field_temp : fields)
					{
						if (field_temp instanceof Street)
						{
								boolean sameGroupColor = ((Street) field_temp).getGroupColor().equals(groupColor);
								if (((Street) field_temp).getOwnerNumber() == ownerNumber && sameGroupColor)
									num++;
						}
						
					}
				}
			}
		}
		return num;
	}
	
	/**
	 * Determines if the player has any pawnable fields.
	 * @param player: Player object
	 * @return
	 */
	public static boolean hasPawnableFields(Player player)
	{
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				// Does the player owns the field?
				if(((Ownable) field_temp).getOwner() == player)
				{
					// Is field not already pawned?
					if(!((Ownable)field_temp).isPawned())
					{
						// Is field a street?
						if(field_temp instanceof Street)
						{
							// Return true, if street has no buildings
							if (((Street) field_temp).getNumOfHotels() == 0 && ((Street) field_temp).getNumOfHouses() == 0)
							{
								return true;
							}
						}
						else
						{
							// The field is a shipping or brewery => always pawnable if not pawned already
							return true;
						}
					}
				}
			}
		}
		// Player owns no pawnable fields
		return false;
	}
	
	/**
	 * Determines if the player any unpawnable fields.
	 * @param player: Player object
	 * @return
	 */
	public static boolean hasUnPawnableFields(Player player)
	{
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				Ownable ownableField = (Ownable)field_temp;
				
				if (ownableField.getOwner() == player && ownableField.isPawned() && (player.getBalance() >= (ownableField.getPrice()/2) * 1.1 ))
				{
					return true;
				}
			}
		}
		// Player owns no unpawnable fields
		return false;
	}
	
	/**
	 * Get all the pawnable fields (owned by the player)
	 * @param player: Player object
	 * @return: CustomArray of Field objects
	 */
	public static CustomArray<Field> getPawnableFields(Player player)
	{
		CustomArray<Field> pawnableFields = new CustomArray<Field>();
		
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				// Does the player owns the field?
				if(((Ownable) field_temp).getOwner() == player)
				{
					// Is field not already pawned?
					if(!((Ownable) field_temp).isPawned())
					{	
						// Is field a street?
						if(field_temp instanceof Street)
						{
							// Add field, if street has no buildings
							if (((Street) field_temp).getNumOfHotels() == 0 && ((Street) field_temp).getNumOfHouses() == 0)
							{
								pawnableFields.add(field_temp);
							}
						}
						
						else
						{
							// Add field, because a shipping or brewery is always pawnable
							pawnableFields.add(field_temp);
						}
					}	
				}
			}
		}
		// Return pawnable fields
		return pawnableFields;		
	}

	/**
	 * Get all unpawnable fields (owned by the player)
	 * @param player: Player object
	 * @return: CustomArray of Field objects
	 */
	public static CustomArray<Field> getUnPawnableFields(Player player)
	{
		CustomArray<Field> unPawnableFields = new CustomArray<Field>();
		
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				Ownable ownableField = (Ownable)field_temp;
			
				// Does the player owns the field?
				if (ownableField.getOwner() == player && ownableField.isPawned() && (player.getBalance() >= (ownableField.getPrice()/2) * 1.1 ))
					unPawnableFields.add(field_temp);
			}
			// Return unPawnable fields
		}
		
		return unPawnableFields;
	}
		
	/**
	 * Determines of the player own any fields which are ready for houses.
	 * @param player: Player object
	 * @return
	 */
	public static boolean hasFieldsWithSellableBuilding(Player player)
	{
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				// Does the player owns the field?
				if(((Ownable) field_temp).getOwner() == player)
				{
					// Is field not already pawned?
					if(!((Ownable) field_temp).isPawned())
					{
						// Is field a street?
						if(field_temp instanceof Street)
						{
							// Return true, if street has any buildings
							if (((Street) field_temp).getNumOfHotels() > 0 || ((Street) field_temp).getNumOfHouses() > 0)
							{
								return true;
							}
						}
					}
				}
			}
		}
		// Player owns no fields with sellable buildings on
		return false;		
	}
	
	/**
	 * Get all fields which has houses built on. (Owned by the player)
	 * @param player: Player object
	 * @return: CustomArray of Field objects.
	 */
	public static CustomArray<Field> getFieldsWithSellableBuilding(Player player)
	{
		CustomArray<Field> sellableFields = new CustomArray<Field>();
		
		// Loop through all fields
		for (Field field_temp : fields)
		{	
			//Is field ownable?
			if (field_temp instanceof Ownable)
			{
				// Does the player owns the field?
				if(((Ownable) field_temp).getOwner() == player)
				{
					// Is field a street?
					if(field_temp instanceof Street)
					{
						// Add field, if street has any buildings
						if (((Street) field_temp).getNumOfHotels() > 0 || ((Street) field_temp).getNumOfHouses() > 0)
						{
							sellableFields.add(field_temp);
						}
					}
				}
			}
		}
		// Return sellable fields
		return sellableFields;	
	}
	
	/**
	 * Determines if the player has any field that are ready to have houses built on. (Owned by the player)
	 * @param player: Player object
	 * @return
	 */
	public static boolean hasBuildableFields(Player player)
	{
		for (Field field_temp : fields)
		{	
			if (field_temp instanceof Street)
			{
				Street checkedStreet = ((Street)field_temp); 
				
				if (player == checkedStreet.getOwner() && checkedStreet.getNumOfHotels() == 0 && player.getBalance() >= checkedStreet.getHousePrice())
				{
					// Is field not pawned?
					if(!((Ownable) field_temp).isPawned())
						return true;
				}	
			}
		}
		return false;
	}
	
	/**
	 * Get all the fields that are ready for houses (owned by the player)
	 * @param player: Player object
	 * @return: CustomArray of Field objects.
	 */
	public static CustomArray<Field> getBuildableFields(Player player)
	{
		CustomArray<Field> buildableFields = new CustomArray<Field>();
		for (Field field : fields)
		{	
			if (field instanceof Street)
			{
				Street checkedStreet = ((Street)field); 
				
				if (player == checkedStreet.getOwner() && checkedStreet.getNumOfHotels() == 0 && player.getBalance() >= checkedStreet.getHousePrice())
				{
					// Is field not pawned?
					if(!((Ownable) field).isPawned())
						buildableFields.add(field);
				}	
			}
		}	
		return buildableFields;
	}
	
	/**
	 * Clears ownership of a specific ownable field
	 * @param player: Player object
	 */
	public static void eraseOwnership(Player player)
	{
		for (Field field : fields)
		{	
			if (player == ((Ownable) field).getOwner())
			{
			 ((Ownable) field).setOwner(null);
			}
		}
	}
    
	/**
	 * Determines if the player has any fields (with no houses) ready to be sold (owned by the player)
	 * @param player: Player object
	 * @return
	 */
	public static boolean hasSellableBuildings(Player player)
	{
		for (Field field_temp : fields)
		{	
			if (field_temp instanceof Street)
			{
				Street checkedStreet = ((Street)field_temp); 
				
				if (player == checkedStreet.getOwner() && (checkedStreet.getNumOfHotels() == 1 || checkedStreet.getNumOfHouses() > 0))
				{
					return true;
				}	
			}
		}
		return false;
	}
	
	/**
	 * Get all the field that has houses built on. (Owned by the player)
	 * @param player: Player object
	 * @return
	 */
	public static CustomArray<Field> getSellableBuildings(Player player)
	{
		CustomArray<Field> sellableBuildings = new CustomArray<Field>();
		for (Field field : fields)
		{	
			if (field instanceof Street)
			{
				Street checkedStreet = ((Street)field); 
				
				if (player == checkedStreet.getOwner() && (checkedStreet.getNumOfHotels() == 1 || checkedStreet.getNumOfHouses() > 0))
				{
				  sellableBuildings.add(field);
				}	
			}
		}
		return sellableBuildings;	
		
	}
	
}